﻿using X23.EFCore.Service;
using X23.Model.Bill;
using X23.Application.BillManagement.Dto;
using AutoMapper;
using X23.EFCore.Repository;
using System.Threading.Tasks;
using System.Linq;
using X23.Util.ContextUtil;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace X23.Application.BillManagement
{
    public class BillManagementService : CRUDService<BillDto, Bill>, IBillManagementService
    {
        private readonly IMapper _mapper;
        private readonly UserContext _currentUser;
        private readonly IRepository<Bill> _billRepository;
        private readonly IRepository<BillAmount> _billAmountRepository;

        public BillManagementService(IRepository<Bill> billRepository, IRepository<BillAmount> billAmountRepository, IMapper mapper) : base(billRepository, mapper)
        {
            _mapper = mapper;
            _currentUser = ContextUtil.CurrentUser;
            _billRepository = billRepository;
            _billAmountRepository = billAmountRepository;
        }
        /// <summary>
        /// 获取指定年月的花销
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<BillAmountDto> GetBillByTime(BillAmountInputDto input)
        {
            var result = (await _billAmountRepository.GetAllAsync()).Where(x => x.Year == input.Year && x.Month == input.Month).ToList();
            var moneyList = (await _billRepository.GetAllAsync())
                .Where(x => x.Year == input.Year && x.Month == input.Month & x.UserId == _currentUser.Id)
                .Select(x => x.Money).ToList();
            if (result.Count > 0)
            {
                return new BillAmountDto
                {
                    Expenditure = result.Sum(x => x.Expenditure),
                    Income = result.Sum(x => x.Income),
                };
            }
            return new BillAmountDto();
        }
        /// <summary>
        /// 获取分组账单
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<List<BillGroupDto>> GetBillGroupList(BillGroupInputDto input)
        {
            var result = new List<BillGroupDto>();
            var billList = new List<Bill>();
            if (input.LastId > 0)
            {
                var sameMore = (await _billRepository.GetAllAsync())
                    .Where(x => x.UserId == _currentUser.Id && x.DateTime == input.DateTime && x.Id < input.LastId)
                    .OrderByDescending(x => x.Id).Take(input.Rows).ToList();
                billList.AddRange(sameMore);
            }
            var nextRows = input.Rows - billList.Count;
            if (nextRows > 0)
            {
                var nextList = (await _billRepository.GetAllAsync())
                    .Where(x => x.UserId == _currentUser.Id && x.DateTime < input.DateTime)
                    .OrderByDescending(x => x.Id)
                    .Take(nextRows)
                    .ToList();
                billList.AddRange(nextList);
            }

            //计算分组后年月日的开销
            if (billList.Count > 0)
            {
                var years = billList.Select(x => x.Year).Distinct().ToList();
                var months = billList.Select(x => x.Month).Distinct().ToList();
                var days = billList.Select(x => x.Day).Distinct().ToList();
                var billAmountList = (await _billAmountRepository.GetAllAsync())
                    .Where(x => years.Contains(x.Year) && months.Contains(x.Month) && days.Contains(x.Day))
                    .ToList();

                result = _mapper.Map<List<BillGroupDto>>(billAmountList);

                result.ForEach(item =>
                {
                    item.BillList = _mapper.Map<List<BillDto>>(billList.Where(x => x.Year == item.Year && x.Day == item.Day && x.Month == item.Month));
                });
            }

            return result;
        }


        public override async Task<BillDto> AddAsync(BillDto entity)
        {
            await AddBillAmount(entity);
            return await base.AddAsync(entity);
        }
        public override async Task<BillDto> UpdateAsync(BillDto entity)
        {
            //先修改旧数据再添加新数据
            await UpdateBillAmount(entity.Id);
            await AddBillAmount(entity);
            return await base.UpdateAsync(entity);
        }
        public override async Task<bool> DeleteAsync(object id, bool isPhysicalDelete = false)
        {
            await UpdateBillAmount(id);
            return await base.DeleteAsync(id, isPhysicalDelete);
        }


        private async Task AddBillAmount(BillDto entity)
        {
            var billAmount = _billAmountRepository.Table().Where(x => x.Year == entity.Year && x.Month == entity.Month && x.Day == entity.Day).FirstOrDefault();
            if (billAmount == null)
            {
                await _billAmountRepository.AddAsync(new BillAmount
                {
                    Year = entity.Year,
                    Month = entity.Month,
                    Day = entity.Day,
                    Income = entity.Money > 0 ? entity.Money : 0,
                    Expenditure = entity.Money < 0 ? entity.Money : 0,
                });
            }
            else
            {
                if (entity.Money > 0)
                {
                    billAmount.Income += entity.Money;
                }
                else
                {
                    billAmount.Expenditure += entity.Money;
                }
                await _billAmountRepository.UpdateAsync(billAmount);
            }
        }

        private async Task UpdateBillAmount(object id)
        {
            var old = await _billRepository.GetByIdAsync(id);
            var oldMony = old.Money;
            var oldBillAmount = _billAmountRepository.Table().Where(x => x.Year == old.Year && x.Month == old.Month && x.Day == old.Day).First();
            if (oldMony > 0)
            {
                oldBillAmount.Income -= oldMony;
            }
            else
            {
                oldBillAmount.Expenditure -= oldMony;
            }
            await _billAmountRepository.UpdateAsync(oldBillAmount);
        }
    }
}
